/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufu.sdg12014.thriftpluresdb.client;

import br.ufu.sdg12014.base.replycoordinator.ThriftReplica;
import br.ufu.sdg12014.base.replycoordinator.Transacao;
import java.io.Serializable;
import java.util.List;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

/**
 *
 * @author SergioPeres
 */
public class DBNode implements Runnable, Serializable
{
    private transient ThriftReplica.Client client;
    public final String address;
    public static final int PORT = 9091;
    public transient Action action;
    private transient TTransport transport = null;
    private transient TProtocol protocol;

    private transient Operation operation;
    private transient int clientToken;
    private transient Transacao transacao;
    private transient int operationResponse;
    public transient boolean connect;

    public DBNode(String s)
    {
        this.address = s;
        this.init();
    }

    public void init()
    {
        try
        {
            this.transport = new TSocket(address, PORT);
            this.transport.open();

            this.protocol = new TBinaryProtocol(transport);
            this.client = new ThriftReplica.Client(protocol);

        } catch (TTransportException x)
        {
            x.printStackTrace();
            this.connect = false;
        }
        this.connect = true;
    }

    public void disconnect()
    {
        try
        {
            this.protocol.reset();
            this.transport.close();
        } catch (Exception e)
        {
            System.err.println("Disconnected");
        } finally
        {
            this.connect = false;
        }
    }

    public synchronized void setOperation(Operation op, int clientID)
    {
        this.operation = op;
        this.clientToken = clientID;
        this.operationResponse = 0;
    }

    public synchronized void setOperation(Operation op, int clientID, Transacao transac)
    {
        this.operation = op;
        this.clientToken = clientID;
        this.transacao = transac;
        this.operationResponse = 0;
    }

    public void run()
    {
        synchronized (this)
        {
            switch (operation)
            {
                case VOTE_REQUEST:
                    this.operationResponse = this.requestVote(this.clientToken, this.transacao) ? 1 : -1;
                    break;
                case PREPARE_COMMIT:
                    this.operationResponse = this.prepareCommit(this.clientToken) ? 1 : -1;
                    break;
                case GLOBAL_COMMIT:
                    this.globalCommit(this.clientToken);
                    break;
                case GLOBAL_ABORT:
                    this.globalAbort(this.clientToken);
                    break;
            }
            this.transacao = null;
            this.operation = null;
        }
    }

    public String enter()
    {
        try
        {
            return client.opEnter();
        } catch (TException ex)
        {
            return "enter: Erro ao tentar criar uma nova entrada!";
        }
    }

    public String updateServerFile(List<String> listaParticipantes)
    {
        try
        {
            return client.opUpdateServerFile(listaParticipantes);
        } catch (TException ex)
        {
            System.err.println("updateServerFile: Erro ao tentar criar uma nova entrada!");
            return null;
        }
    }

    public List<Transacao> getCommitted(int counter)
    {
        try
        {
            return client.getCommitted(counter);
        } catch (TException ex)
        {
            System.err.println("updateServerFile: Erro ao tentar criar uma nova entrada!");
            return null;
        }
    }

    public boolean globalAbort(int clientToken)
    {
        try
        {
            return client.opGlobalAbort(clientToken);
        } catch (TException ex)
        {
            System.err.println("globalAbort: Erro ao tentar ler uma entrada!");
            return false;
        }
    }

    public boolean globalCommit(int clientToken)
    {
        try
        {
            return client.opGlobalCommit(clientToken);
        } catch (TException ex)
        {
            System.err.println("globalCommit: Erro");
            return false;
        }
    }

    public boolean prepareCommit(int clientToken)
    {
        try
        {
            return client.opPrepareCommit(clientToken);
        } catch (TException ex)
        {
            System.err.println("prepareCommit: Erro");
            return false;
        }
    }

    public boolean requestVote(int clientToken, Transacao transacao)
    {
        try
        {
            return client.opRequestVote(clientToken, transacao);
        } catch (TException ex)
        {
            System.err.println("[NET] COMMIT: Vote-Request Error");
            this.connect = false;
            return false;
        }
    }

    public int getOperationResponse()
    {
        return operationResponse;
    }

    public enum Operation
    {
        VOTE_REQUEST, PREPARE_COMMIT, GLOBAL_COMMIT, GLOBAL_ABORT
    }
}
